#include "bsp_timer_stm32f103.h"
#include "bsp_common_stm32f103.h"
#include "stm32f1xx_ll_bus.h"

Bsp_TTimerUnion g_bsp_timer_union;

void TIM6_IRQHandler(void)
{
    Int32 i; 
    Bsp_TTimerUnion* self = &g_bsp_timer_union;

    LL_TIM_ClearFlag_UPDATE(TIM6);  // 清溢出中断

    self->tick_count++;
    __DMB();

    for (i = 0; i < self->count; i++)
    {
        Bsp_TTimerItem *timer = &self->items[i];

        if (timer->enable)
        {
            timer->tick_count++;

            if (timer->tick_count >= timer->interval)
            {
                if (timer->on_timer != NULL)
                    timer->on_timer(timer);

                timer->tick_count = 0;
            }
        }
    }
}

static void Bsp_TimerItemInit_(Bsp_TTimerItem* item)
{
    item->is_used = False;
    item->enable = False;
    item->interval = 0;
    item->tick_count = 0;
    item->on_timer = NULL;
    item->user_data = NULL;
}

static void Bsp_TimerItemDone_(Bsp_TTimerItem* item)
{
    item->is_used = False;
    item->enable = False;
    item->interval = 0;
    item->tick_count = 0;
    item->on_timer = NULL;
    item->user_data = NULL;
}

void Bsp_TimerInit_(void)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;

    self->tick_count = 0;
    self->is_open = False;

    for (Int32 i = 0; i < BSP_TIMER_ITEM_MAX_COUNT; i++)
        Bsp_TimerItemInit_(&self->items[i]);

    self->count = 0;
}

void Bsp_TimerDone_(void)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    
    Bsp_TimerClose_();
    
    for (Int32 i = 0; i < BSP_TIMER_ITEM_MAX_COUNT; i++)
        Bsp_TimerItemDone_(&self->items[i]);
}

void Bsp_TimerOpen_(void)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    if (!self->is_open)
    {
        LL_TIM_InitTypeDef TIM_InitStruct = {0};

        /* Peripheral clock enable */
        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM6);

        /* TIM6 interrupt Init */
        NVIC_SetPriority(TIM6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
        NVIC_EnableIRQ(TIM6_IRQn);

        /// 定时1ms中断
        TIM_InitStruct.Prescaler = 35;
        TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
        TIM_InitStruct.Autoreload = 999;
        LL_TIM_Init(TIM6, &TIM_InitStruct);
        LL_TIM_DisableARRPreload(TIM6);
        LL_TIM_SetTriggerOutput(TIM6, LL_TIM_TRGO_RESET);
        LL_TIM_DisableMasterSlaveMode(TIM6);

        LL_TIM_EnableIT_UPDATE(TIM6);   ///< 使能溢出中断
        LL_TIM_ClearFlag_UPDATE(TIM6);  ///< 清标志

        /// 使能定时器
        LL_TIM_EnableCounter(TIM6);

        self->is_open = True;
    }
}

void Bsp_TimerClose_(void)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    if (self->is_open)
    {
        /// 关闭所有item
        for (Int32 i = 0; i < BSP_TIMER_ITEM_MAX_COUNT; i++)
            self->items[i].enable = False;

        LL_TIM_DisableIT_UPDATE(TIM6);   ///< 关闭溢出中断

        /// 关闭定时器
        LL_TIM_DisableCounter(TIM6);

        /// 关中断
        NVIC_DisableIRQ(TIM6_IRQn);
        
        /// 关外设时钟
        LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM6);
        
        self->is_open = False;
    }
}

/// 获取一个空闲的定时器
static Bsp_TTimerItem* Bsp_TimerGetFreeItem_(Bsp_TTimerUnion* self)
{
    Bsp_TTimerItem* item = &self->items[0];

    for (Int32 i = 1; i < BSP_TIMER_ITEM_MAX_COUNT && item->is_used; i++)
        item = &self->items[i];

    return item->is_used ? NULL : item;
}

static Bool Bsp_TimerAssertHandle(Bsp_TTimerUnion* self, Bsp_THandle handle)
{
    Bool is_ok = False;
    Bsp_TTimerItem* item = &self->items[0];
    Bsp_TTimerItem* item_end = item + BSP_TIMER_ITEM_MAX_COUNT;

    for (; item < item_end && !is_ok; item++)
        is_ok = item->is_used && (void*)item == (void*)handle;

    return is_ok;
}

UInt32 Bsp_TimerGetTick(void)
{
    return g_bsp_timer_union.tick_count;
}

Int32 Bsp_TimerGetMaxCount(void)
{
    return BSP_TIMER_ITEM_MAX_COUNT;
}

Int32 Bsp_TimerGetCount(void)
{
    return BSP_TIMER_ITEM_MAX_COUNT - g_bsp_timer_union.count;
}

Bsp_THandle Bsp_TimerCreate(void)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bsp_TTimerItem* item = Bsp_TimerGetFreeItem_(self);

    if (item != NULL)
    {
        Bsp_TimerItemInit_(item);
        item->is_used = True;
        self->count++;
    }

    return item;
}

void Bsp_TimerFree(Bsp_THandle handle)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TimerItemDone_((Bsp_TTimerItem*)handle);
        self->count--;
    }
}

void* Bsp_TimerGetUserData(Bsp_THandle handle)
{
    void* result;
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TTimerItem* item = (Bsp_TTimerItem*)handle;
        result = item->user_data;
    }
    else
        result = NULL;

    return result;
}

void Bsp_TimerSetUserData(Bsp_THandle handle, void* user_data)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TTimerItem* item = (Bsp_TTimerItem*)handle;
        item->user_data = user_data;
    }
}

void Bsp_TimerSetEvent(Bsp_THandle handle, Bsp_TOnTimerEvent on_event)
{
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TTimerItem* item = (Bsp_TTimerItem*)handle;
        item->on_timer = on_event;
    }
}

Int32 Bsp_TimerStart(Bsp_THandle handle, UInt32 timeout)
{
    Int32 result;
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TTimerItem* item = (Bsp_TTimerItem*)handle;

        if (!item->enable)
        {
            item->interval = timeout;
            item->tick_count = 0;
            item->enable = True;
            
            result = Bsp_kErrorCodeSuccess;
        }
        else
            result = Bsp_kErrorCodeProcess; ///< 需要先stop
    }
    else
        result = Bsp_kErrorCodeParam;

    return result;
}

Int32 Bsp_TimerStop(Bsp_THandle handle)
{
    Int32 result;
    Bsp_TTimerUnion* self = &g_bsp_timer_union;
    Bool is_ok;
    is_ok = Bsp_TimerAssertHandle(self, handle);

    if (is_ok)
    {
        Bsp_TTimerItem* item = (Bsp_TTimerItem*)handle;

        if (item->enable)
        {
            item->enable = False;
            
            result = Bsp_kErrorCodeSuccess;
        }
        else
            result = Bsp_kErrorCodeProcess; ///< 需要先start
    }
    else
        result = Bsp_kErrorCodeParam;

    return result;
}
